#pragma once

#include <deque>
#include <mutex>
#include <condition_variable>

template<typename Data>
class ConcurrentQueue
{
private:
	std::deque<Data> mQueue;
	mutable std::mutex mMutex;
	std::condition_variable mCondition;

public:
	void push(Data const& data)
	{
		mMutex.lock();
		mQueue.push_back(data);
		mMutex.unlock();

		mCondition.notify_one();
	}

	bool empty() const
	{
		mMutex.lock();
		bool isEmpty = mQueue.empty();
		mMutex.unlock();

		return isEmpty;
	}

	bool try_pop(Data& poppedValue)
	{
		mMutex.lock();
		if (mQueue.empty())
			return false;

		poppedValue = mQueue.front();
		mQueue.pop();
		mMutex.unlock();
		return true;
	}

	void wait_and_pop(Data& popped_value)
	{
		mMutex.lock();
		while (mQueue.empty())
			mCondition.wait(mMutex);

		popped_value = mQueue.front();
		mQueue.pop();
		mMutex.unlock();
	}

	void transfer(std::deque<Data>& queue)
	{
		mMutex.lock();
		queue = std::deque<Data>(mQueue);
		mQueue.clear();
		mMutex.unlock();
	}
};